Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Displaying Raw • Download


electron/main.js v4.4.0-rc.1 (3e06f582) Text, 21.85 KB

Tff7b72const Tb4b4b4{
Te6edf3appTb4b4b4,
Te6edf3BrowserWindowTb4b4b4,
Te6edf3dialogTb4b4b4,
Te6edf3ipcMainTb4b4b4,
Te6edf3shellTb4b4b4,
Te6edf3systemPreferencesTb4b4b4,
Te6edf3TrayTb4b4b4,
Te6edf3MenuTb4b4b4,
Te6edf3NotificationTb4b4b4,
Te6edf3powerSaveBlockerTb4b4b4,
Te6edf3sessionTb4b4b4,
Tb4b4b4} Tff7b72= Te6edf3requireTb4b4b4(Ta5d6ff"electron"Tb4b4b4)Tb4b4b4;
Tff7b72const Te6edf3electronPrompt Tff7b72= Te6edf3requireTb4b4b4(Ta5d6ff"electron-prompt"Tb4b4b4)Tb4b4b4;
Tff7b72const Tb4b4b4{ Te6edf3spawn Tb4b4b4} Tff7b72= Te6edf3requireTb4b4b4(Ta5d6ff"child_process"Tb4b4b4)Tb4b4b4;
Tff7b72const Te6edf3fs Tff7b72= Te6edf3requireTb4b4b4(Ta5d6ff"fs"Tb4b4b4)Tb4b4b4;
Tff7b72const Te6edf3path Tff7b72= Te6edf3requireTb4b4b4(Ta5d6ff"node:path"Tb4b4b4)Tb4b4b4;

Tff7b72const Te6edf3crypto Tff7b72= Te6edf3requireTb4b4b4(Ta5d6ff"crypto"Tb4b4b4)Tb4b4b4;

T8b949e// remember main window
Tff7b72var Te6edf3mainWindow Tff7b72= Tff7b72nullTb4b4b4;

T8b949e// tray instance
Tff7b72var Te6edf3tray Tff7b72= Tff7b72nullTb4b4b4;

T8b949e// power save blocker id
Tff7b72var Te6edf3activePowerSaveBlockerId Tff7b72= Tff7b72nullTb4b4b4;

T8b949e// track if we are actually quiting
Tff7b72var Te6edf3isQuiting Tff7b72= Tff7b72falseTb4b4b4;

T8b949e// remember child process for exe so we can kill it when app exits
Tff7b72var Te6edf3exeChildProcess Tff7b72= Tff7b72nullTb4b4b4;

T8b949e// store integrity status
Tff7b72var Te6edf3integrityStatus Tff7b72= Tb4b4b4{
Te6edf3backendTff7b72: Tb4b4b4{ Te6edf3okTff7b72: Tff7b72trueTb4b4b4, Te6edf3issuesTff7b72: Tb4b4b4[Tb4b4b4] Tb4b4b4}Tb4b4b4,
Te6edf3dataTff7b72: Tb4b4b4{ Te6edf3okTff7b72: Tff7b72trueTb4b4b4, Te6edf3issuesTff7b72: Tb4b4b4[Tb4b4b4] Tb4b4b4}Tb4b4b4,
Tb4b4b4}Tb4b4b4;

T8b949e// Check for hardware acceleration preference in storage dir
Tff7b72try Tb4b4b4{
Tff7b72const Te6edf3storageDir Tff7b72= Te6edf3getDefaultStorageDirTb4b4b4(Tb4b4b4)Tb4b4b4;
Tff7b72const Te6edf3disableGpuFile Tff7b72= Te6edf3pathTb4b4b4.Te6edf3joinTb4b4b4(Te6edf3storageDirTb4b4b4, Ta5d6ff"disable-gpu"Tb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3fsTb4b4b4.Te6edf3existsSyncTb4b4b4(Te6edf3disableGpuFileTb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3appTb4b4b4.Te6edf3disableHardwareAccelerationTb4b4b4(Tb4b4b4)Tb4b4b4;
Te6edf3consoleTb4b4b4.Te6edf3logTb4b4b4(Ta5d6ff"Hardware acceleration disabled via storage flag."Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Tb4b4b4} Tff7b72catch Tb4b4b4{
T8b949e// ignore errors reading storage dir this early
Tb4b4b4}

T8b949e// Handle hardware acceleration disabling via CLI
Tff7b72if Tb4b4b4(Te6edf3processTb4b4b4.Te6edf3argvTb4b4b4.Te6edf3includesTb4b4b4(Ta5d6ff"--disable-gpu"Tb4b4b4) Tff7b72|| Te6edf3processTb4b4b4.Te6edf3argvTb4b4b4.Te6edf3includesTb4b4b4(Ta5d6ff"--disable-software-rasterizer"Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3appTb4b4b4.Te6edf3disableHardwareAccelerationTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4}

T8b949e// Protocol registration
Tff7b72if Tb4b4b4(Te6edf3processTb4b4b4.Te6edf3defaultAppTb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3processTb4b4b4.Te6edf3argvTb4b4b4.Te6edf3length Tff7b72>= T79c0ff2Tb4b4b4) Tb4b4b4{
Te6edf3appTb4b4b4.Te6edf3setAsDefaultProtocolClientTb4b4b4(Ta5d6ff"lxmf"Tb4b4b4, Te6edf3processTb4b4b4.Te6edf3execPathTb4b4b4, Tb4b4b4[Te6edf3pathTb4b4b4.Te6edf3resolveTb4b4b4(Te6edf3processTb4b4b4.Te6edf3argvTb4b4b4[T79c0ff1Tb4b4b4]Tb4b4b4)Tb4b4b4]Tb4b4b4)Tb4b4b4;
Te6edf3appTb4b4b4.Te6edf3setAsDefaultProtocolClientTb4b4b4(Ta5d6ff"rns"Tb4b4b4, Te6edf3processTb4b4b4.Te6edf3execPathTb4b4b4, Tb4b4b4[Te6edf3pathTb4b4b4.Te6edf3resolveTb4b4b4(Te6edf3processTb4b4b4.Te6edf3argvTb4b4b4[T79c0ff1Tb4b4b4]Tb4b4b4)Tb4b4b4]Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3appTb4b4b4.Te6edf3setAsDefaultProtocolClientTb4b4b4(Ta5d6ff"lxmf"Tb4b4b4)Tb4b4b4;
Te6edf3appTb4b4b4.Te6edf3setAsDefaultProtocolClientTb4b4b4(Ta5d6ff"rns"Tb4b4b4)Tb4b4b4;
Tb4b4b4}

T8b949e// Single instance lock
Tff7b72const Te6edf3gotTheLock Tff7b72= Te6edf3appTb4b4b4.Te6edf3requestSingleInstanceLockTb4b4b4(Tb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3gotTheLockTb4b4b4) Tb4b4b4{
Te6edf3appTb4b4b4.Te6edf3quitTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3appTb4b4b4.Te6edf3onTb4b4b4(Ta5d6ff"second-instance"Tb4b4b4, Tb4b4b4(Te6edf3eventTb4b4b4, Te6edf3commandLineTb4b4b4) Tb4b4b4=> Tb4b4b4{
T8b949e// Someone tried to run a second instance, we should focus our window.
Tff7b72if Tb4b4b4(Te6edf3mainWindowTb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3mainWindowTb4b4b4.Te6edf3isMinimizedTb4b4b4(Tb4b4b4)Tb4b4b4) Te6edf3mainWindowTb4b4b4.Te6edf3restoreTb4b4b4(Tb4b4b4)Tb4b4b4;
Te6edf3mainWindowTb4b4b4.Te6edf3showTb4b4b4(Tb4b4b4)Tb4b4b4;
Te6edf3mainWindowTb4b4b4.Te6edf3focusTb4b4b4(Tb4b4b4)Tb4b4b4;

T8b949e// Handle protocol links from second instance
Tff7b72const Te6edf3url Tff7b72= Te6edf3commandLineTb4b4b4.Te6edf3popTb4b4b4(Tb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3url Tff7b72&& Tb4b4b4(Te6edf3urlTb4b4b4.Te6edf3startsWithTb4b4b4(Ta5d6ff"lxmf://"Tb4b4b4) Tff7b72|| Te6edf3urlTb4b4b4.Te6edf3startsWithTb4b4b4(Ta5d6ff"rns://"Tb4b4b4)Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3mainWindowTb4b4b4.Te6edf3webContentsTb4b4b4.Te6edf3sendTb4b4b4(Ta5d6ff"open-protocol-link"Tb4b4b4, Te6edf3urlTb4b4b4)Tb4b4b4;
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}Tb4b4b4)Tb4b4b4;
Tb4b4b4}

T8b949e// Handle protocol links on macOS
Te6edf3appTb4b4b4.Te6edf3onTb4b4b4(Ta5d6ff"open-url"Tb4b4b4, Tb4b4b4(Te6edf3eventTb4b4b4, Te6edf3urlTb4b4b4) Tb4b4b4=> Tb4b4b4{
Te6edf3eventTb4b4b4.Te6edf3preventDefaultTb4b4b4(Tb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3mainWindowTb4b4b4) Tb4b4b4{
Te6edf3mainWindowTb4b4b4.Te6edf3showTb4b4b4(Tb4b4b4)Tb4b4b4;
Te6edf3mainWindowTb4b4b4.Te6edf3webContentsTb4b4b4.Te6edf3sendTb4b4b4(Ta5d6ff"open-protocol-link"Tb4b4b4, Te6edf3urlTb4b4b4)Tb4b4b4;
Tb4b4b4}
Tb4b4b4}Tb4b4b4)Tb4b4b4;

Tff7b72function Te6edf3verifyBackendIntegrityTb4b4b4(Te6edf3exeDirTb4b4b4) Tb4b4b4{
Tff7b72const Te6edf3manifestPath Tff7b72= Te6edf3pathTb4b4b4.Te6edf3joinTb4b4b4(Te6edf3exeDirTb4b4b4, Ta5d6ff"backend-manifest.json"Tb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3fsTb4b4b4.Te6edf3existsSyncTb4b4b4(Te6edf3manifestPathTb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3logTb4b4b4(Ta5d6ff"Backend integrity manifest missing, skipping check."Tb4b4b4)Tb4b4b4;
Tff7b72return Tb4b4b4{ Te6edf3okTff7b72: Tff7b72trueTb4b4b4, Te6edf3issuesTff7b72: Tb4b4b4[Ta5d6ff"Manifest missing"Tb4b4b4] Tb4b4b4}Tb4b4b4;
Tb4b4b4}

Tff7b72try Tb4b4b4{
Tff7b72const Te6edf3manifest Tff7b72= Tffa657JSONTb4b4b4.Te6edf3parseTb4b4b4(Te6edf3fsTb4b4b4.Te6edf3readFileSyncTb4b4b4(Te6edf3manifestPathTb4b4b4, Ta5d6ff"utf8"Tb4b4b4)Tb4b4b4)Tb4b4b4;
Tff7b72const Te6edf3issues Tff7b72= Tb4b4b4[Tb4b4b4]Tb4b4b4;

Tff7b72const Te6edf3filesToVerify Tff7b72= Te6edf3manifestTb4b4b4.Te6edf3files Tff7b72|| Te6edf3manifestTb4b4b4;
Tff7b72const Te6edf3metadata Tff7b72= Te6edf3manifestTb4b4b4.Te6edf3_metadata Tff7b72|| Tb4b4b4{Tb4b4b4}Tb4b4b4;

T8b949e// The exeDir is build/exe when running or unpacked
T8b949e// we only care about files in the manifest
Tff7b72for Tb4b4b4(Tff7b72const Tb4b4b4[Te6edf3relPathTb4b4b4, Te6edf3expectedHashTb4b4b4] Tff7b72of Tffa657ObjectTb4b4b4.Te6edf3entriesTb4b4b4(Te6edf3filesToVerifyTb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72const Te6edf3fullPath Tff7b72= Te6edf3pathTb4b4b4.Te6edf3joinTb4b4b4(Te6edf3exeDirTb4b4b4, Te6edf3relPathTb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3fsTb4b4b4.Te6edf3existsSyncTb4b4b4(Te6edf3fullPathTb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3issuesTb4b4b4.Te6edf3pushTb4b4b4(Ta5d6ff`Ta5d6ffMissing: Tffd700${Te6edf3relPathTffd700}Ta5d6ff`Tb4b4b4)Tb4b4b4;
Tff7b72continueTb4b4b4;
Tb4b4b4}

Tff7b72const Te6edf3fileBuffer Tff7b72= Te6edf3fsTb4b4b4.Te6edf3readFileSyncTb4b4b4(Te6edf3fullPathTb4b4b4)Tb4b4b4;
Tff7b72const Te6edf3actualHash Tff7b72= Te6edf3cryptoTb4b4b4.Te6edf3createHashTb4b4b4(Ta5d6ff"sha256"Tb4b4b4)Tb4b4b4.Te6edf3updateTb4b4b4(Te6edf3fileBufferTb4b4b4)Tb4b4b4.Te6edf3digestTb4b4b4(Ta5d6ff"hex"Tb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3actualHash Tff7b72!== Te6edf3expectedHashTb4b4b4) Tb4b4b4{
Te6edf3issuesTb4b4b4.Te6edf3pushTb4b4b4(Ta5d6ff`Ta5d6ffModified: Tffd700${Te6edf3relPathTffd700}Ta5d6ff`Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Tb4b4b4}

Tff7b72if Tb4b4b4(Te6edf3issuesTb4b4b4.Te6edf3length Tff7b72> T79c0ff0 Tff7b72&& Te6edf3metadataTb4b4b4.Te6edf3date Tff7b72&& Te6edf3metadataTb4b4b4.Te6edf3timeTb4b4b4) Tb4b4b4{
Te6edf3issuesTb4b4b4.Te6edf3unshiftTb4b4b4(Ta5d6ff`Ta5d6ffBackend build timestamp: Tffd700${Te6edf3metadataTb4b4b4.Te6edf3dateTffd700}Ta5d6ff Tffd700${Te6edf3metadataTb4b4b4.Te6edf3timeTffd700}Ta5d6ff`Tb4b4b4)Tb4b4b4;
Tb4b4b4}

Tff7b72return Tb4b4b4{
Te6edf3okTff7b72: Te6edf3issuesTb4b4b4.Te6edf3length Tff7b72=== T79c0ff0Tb4b4b4,
Te6edf3issuesTff7b72: Te6edf3issuesTb4b4b4,
Tb4b4b4}Tb4b4b4;
Tb4b4b4} Tff7b72catch Tb4b4b4(Te6edf3errorTb4b4b4) Tb4b4b4{
Te6edf3logTb4b4b4(Ta5d6ff`Ta5d6ffBackend integrity check failed: Tffd700${Te6edf3errorTb4b4b4.Te6edf3messageTffd700}Ta5d6ff`Tb4b4b4)Tb4b4b4;
Tff7b72return Tb4b4b4{ Te6edf3okTff7b72: Tff7b72falseTb4b4b4, Te6edf3issuesTff7b72: Tb4b4b4[Te6edf3errorTb4b4b4.Te6edf3messageTb4b4b4] Tb4b4b4}Tb4b4b4;
Tb4b4b4}
Tb4b4b4}

T8b949e// allow fetching app version via ipc
Te6edf3ipcMainTb4b4b4.Te6edf3handleTb4b4b4(Ta5d6ff"app-version"Tb4b4b4, Tb4b4b4(Tb4b4b4) Tb4b4b4=> Tb4b4b4{
Tff7b72return Te6edf3appTb4b4b4.Te6edf3getVersionTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4}Tb4b4b4)Tb4b4b4;

T8b949e// allow fetching hardware acceleration status via ipc
Te6edf3ipcMainTb4b4b4.Te6edf3handleTb4b4b4(Ta5d6ff"is-hardware-acceleration-enabled"Tb4b4b4, Tb4b4b4(Tb4b4b4) Tb4b4b4=> Tb4b4b4{
Tff7b72return Te6edf3appTb4b4b4.Te6edf3isHardwareAccelerationEnabledTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4}Tb4b4b4)Tb4b4b4;

T8b949e// allow fetching integrity status
Te6edf3ipcMainTb4b4b4.Te6edf3handleTb4b4b4(Ta5d6ff"get-integrity-status"Tb4b4b4, Tb4b4b4(Tb4b4b4) Tb4b4b4=> Tb4b4b4{
Tff7b72return Te6edf3integrityStatusTb4b4b4;
Tb4b4b4}Tb4b4b4)Tb4b4b4;

T8b949e// Native Notification IPC
Te6edf3ipcMainTb4b4b4.Te6edf3handleTb4b4b4(Ta5d6ff"show-notification"Tb4b4b4, Tb4b4b4(Te6edf3eventTb4b4b4, Tb4b4b4{ Te6edf3titleTb4b4b4, Te6edf3bodyTb4b4b4, Te6edf3silent Tb4b4b4}Tb4b4b4) Tb4b4b4=> Tb4b4b4{
Tff7b72const Te6edf3notification Tff7b72= Tff7b72new Te6edf3NotificationTb4b4b4(Tb4b4b4{
Te6edf3titleTff7b72: Te6edf3titleTb4b4b4,
Te6edf3bodyTff7b72: Te6edf3bodyTb4b4b4,
Te6edf3silentTff7b72: Te6edf3silentTb4b4b4,
Tb4b4b4}Tb4b4b4)Tb4b4b4;
Te6edf3notificationTb4b4b4.Te6edf3showTb4b4b4(Tb4b4b4)Tb4b4b4;

Te6edf3notificationTb4b4b4.Te6edf3onTb4b4b4(Ta5d6ff"click"Tb4b4b4, Tb4b4b4(Tb4b4b4) Tb4b4b4=> Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3mainWindowTb4b4b4) Tb4b4b4{
Te6edf3mainWindowTb4b4b4.Te6edf3showTb4b4b4(Tb4b4b4)Tb4b4b4;
Te6edf3mainWindowTb4b4b4.Te6edf3focusTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Tb4b4b4}Tb4b4b4)Tb4b4b4;
Tb4b4b4}Tb4b4b4)Tb4b4b4;

T8b949e// Power Management IPC
Te6edf3ipcMainTb4b4b4.Te6edf3handleTb4b4b4(Ta5d6ff"set-power-save-blocker"Tb4b4b4, Tb4b4b4(Te6edf3eventTb4b4b4, Te6edf3enabledTb4b4b4) Tb4b4b4=> Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3enabledTb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3activePowerSaveBlockerId Tff7b72=== Tff7b72nullTb4b4b4) Tb4b4b4{
Te6edf3activePowerSaveBlockerId Tff7b72= Te6edf3powerSaveBlockerTb4b4b4.Te6edf3startTb4b4b4(Ta5d6ff"prevent-app-suspension"Tb4b4b4)Tb4b4b4;
Te6edf3logTb4b4b4(Ta5d6ff"Power save blocker started."Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Tb4b4b4} Tff7b72else Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3activePowerSaveBlockerId Tff7b72!== Tff7b72nullTb4b4b4) Tb4b4b4{
Te6edf3powerSaveBlockerTb4b4b4.Te6edf3stopTb4b4b4(Te6edf3activePowerSaveBlockerIdTb4b4b4)Tb4b4b4;
Te6edf3activePowerSaveBlockerId Tff7b72= Tff7b72nullTb4b4b4;
Te6edf3logTb4b4b4(Ta5d6ff"Power save blocker stopped."Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Tb4b4b4}
Tff7b72return Te6edf3activePowerSaveBlockerId Tff7b72!== Tff7b72nullTb4b4b4;
Tb4b4b4}Tb4b4b4)Tb4b4b4;

T8b949e// ignore ssl errors
Te6edf3appTb4b4b4.Te6edf3commandLineTb4b4b4.Te6edf3appendSwitchTb4b4b4(Ta5d6ff"ignore-certificate-errors"Tb4b4b4)Tb4b4b4;

T8b949e// add support for showing an alert window via ipc
Te6edf3ipcMainTb4b4b4.Te6edf3handleTb4b4b4(Ta5d6ff"alert"Tb4b4b4, Tff7b72async Tb4b4b4(Te6edf3eventTb4b4b4, Te6edf3messageTb4b4b4) Tb4b4b4=> Tb4b4b4{
Tff7b72return Tff7b72await Te6edf3dialogTb4b4b4.Te6edf3showMessageBoxTb4b4b4(Te6edf3mainWindowTb4b4b4, Tb4b4b4{
Te6edf3messageTff7b72: Te6edf3messageTb4b4b4,
Tb4b4b4}Tb4b4b4)Tb4b4b4;
Tb4b4b4}Tb4b4b4)Tb4b4b4;

T8b949e// add support for showing a confirm window via ipc
Te6edf3ipcMainTb4b4b4.Te6edf3handleTb4b4b4(Ta5d6ff"confirm"Tb4b4b4, Tff7b72async Tb4b4b4(Te6edf3eventTb4b4b4, Te6edf3messageTb4b4b4) Tb4b4b4=> Tb4b4b4{
T8b949e// show confirm dialog
Tff7b72const Te6edf3result Tff7b72= Tff7b72await Te6edf3dialogTb4b4b4.Te6edf3showMessageBoxTb4b4b4(Te6edf3mainWindowTb4b4b4, Tb4b4b4{
Te6edf3typeTff7b72: Ta5d6ff"question"Tb4b4b4,
Te6edf3titleTff7b72: Ta5d6ff"Confirm"Tb4b4b4,
Te6edf3messageTff7b72: Te6edf3messageTb4b4b4,
Te6edf3cancelIdTff7b72: T79c0ff0Tb4b4b4, T8b949e// esc key should press cancel button
Te6edf3defaultIdTff7b72: T79c0ff1Tb4b4b4, T8b949e// enter key should press ok button
Te6edf3buttonsTff7b72: Tb4b4b4[
Ta5d6ff"Cancel"Tb4b4b4, T8b949e// 0
Ta5d6ff"OK"Tb4b4b4, T8b949e// 1
Tb4b4b4]Tb4b4b4,
Tb4b4b4}Tb4b4b4)Tb4b4b4;

T8b949e// check if user clicked OK
Tff7b72return Te6edf3resultTb4b4b4.Te6edf3response Tff7b72=== T79c0ff1Tb4b4b4;
Tb4b4b4}Tb4b4b4)Tb4b4b4;

T8b949e// add support for showing a prompt window via ipc
Te6edf3ipcMainTb4b4b4.Te6edf3handleTb4b4b4(Ta5d6ff"prompt"Tb4b4b4, Tff7b72async Tb4b4b4(Te6edf3eventTb4b4b4, Te6edf3messageTb4b4b4) Tb4b4b4=> Tb4b4b4{
Tff7b72return Tff7b72await Te6edf3electronPromptTb4b4b4(Tb4b4b4{
Te6edf3titleTff7b72: Te6edf3messageTb4b4b4,
Te6edf3labelTff7b72: Ta5d6ff""Tb4b4b4,
Te6edf3valueTff7b72: Ta5d6ff""Tb4b4b4,
Te6edf3typeTff7b72: Ta5d6ff"input"Tb4b4b4,
Te6edf3inputAttrsTff7b72: Tb4b4b4{
Te6edf3typeTff7b72: Ta5d6ff"text"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4}Tb4b4b4)Tb4b4b4;
Tb4b4b4}Tb4b4b4)Tb4b4b4;

T8b949e// allow relaunching app via ipc
Te6edf3ipcMainTb4b4b4.Te6edf3handleTb4b4b4(Ta5d6ff"relaunch"Tb4b4b4, Tb4b4b4(Tb4b4b4) Tb4b4b4=> Tb4b4b4{
Te6edf3appTb4b4b4.Te6edf3relaunchTb4b4b4(Tb4b4b4)Tb4b4b4;
Te6edf3appTb4b4b4.Te6edf3exitTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4}Tb4b4b4)Tb4b4b4;

Te6edf3ipcMainTb4b4b4.Te6edf3handleTb4b4b4(Ta5d6ff"relaunch-emergency"Tb4b4b4, Tb4b4b4(Tb4b4b4) Tb4b4b4=> Tb4b4b4{
Te6edf3appTb4b4b4.Te6edf3relaunchTb4b4b4(Tb4b4b4{ Te6edf3argsTff7b72: Te6edf3processTb4b4b4.Te6edf3argvTb4b4b4.Te6edf3sliceTb4b4b4(T79c0ff1Tb4b4b4)Tb4b4b4.Te6edf3concatTb4b4b4(Tb4b4b4[Ta5d6ff"--emergency"Tb4b4b4]Tb4b4b4) Tb4b4b4}Tb4b4b4)Tb4b4b4;
Te6edf3appTb4b4b4.Te6edf3exitTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4}Tb4b4b4)Tb4b4b4;

Te6edf3ipcMainTb4b4b4.Te6edf3handleTb4b4b4(Ta5d6ff"shutdown"Tb4b4b4, Tb4b4b4(Tb4b4b4) Tb4b4b4=> Tb4b4b4{
Te6edf3quitTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4}Tb4b4b4)Tb4b4b4;

Te6edf3ipcMainTb4b4b4.Te6edf3handleTb4b4b4(Ta5d6ff"get-memory-usage"Tb4b4b4, Tff7b72async Tb4b4b4(Tb4b4b4) Tb4b4b4=> Tb4b4b4{
Tff7b72return Te6edf3processTb4b4b4.Te6edf3getProcessMemoryInfoTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4}Tb4b4b4)Tb4b4b4;

T8b949e// allow showing a file path in os file manager
Te6edf3ipcMainTb4b4b4.Te6edf3handleTb4b4b4(Ta5d6ff"showPathInFolder"Tb4b4b4, Tb4b4b4(Te6edf3eventTb4b4b4, Te6edf3pathTb4b4b4) Tb4b4b4=> Tb4b4b4{
Te6edf3shellTb4b4b4.Te6edf3showItemInFolderTb4b4b4(Te6edf3pathTb4b4b4)Tb4b4b4;
Tb4b4b4}Tb4b4b4)Tb4b4b4;

Tff7b72function Te6edf3logTb4b4b4(Te6edf3messageTb4b4b4) Tb4b4b4{
T8b949e// log to stdout of this process
Te6edf3consoleTb4b4b4.Te6edf3logTb4b4b4(Te6edf3messageTb4b4b4)Tb4b4b4;

T8b949e// make sure main window exists
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3mainWindowTb4b4b4) Tb4b4b4{
Tff7b72returnTb4b4b4;
Tb4b4b4}

T8b949e// make sure window is not destroyed
Tff7b72if Tb4b4b4(Te6edf3mainWindowTb4b4b4.Te6edf3isDestroyedTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72returnTb4b4b4;
Tb4b4b4}

T8b949e// log to web console
Te6edf3mainWindowTb4b4b4.Te6edf3webContentsTb4b4b4.Te6edf3sendTb4b4b4(Ta5d6ff"log"Tb4b4b4, Te6edf3messageTb4b4b4)Tb4b4b4;
Tb4b4b4}

Tff7b72function Te6edf3getDefaultStorageDirTb4b4b4(Tb4b4b4) Tb4b4b4{
T8b949e// if we are running a windows portable exe, we want to use .reticulum-meshchat in the portable exe dir
T8b949e// e.g if we launch "E:\Some\Path\MeshChat.exe" we want to use "E:\Some\Path\.reticulum-meshchat"
Tff7b72const Te6edf3portableExecutableDir Tff7b72= Te6edf3processTb4b4b4.Te6edf3envTb4b4b4.Te6edf3PORTABLE_EXECUTABLE_DIRTb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3processTb4b4b4.Te6edf3platform Tff7b72=== Ta5d6ff"win32" Tff7b72&& Te6edf3portableExecutableDir Tff7b72!= Tff7b72nullTb4b4b4) Tb4b4b4{
Tff7b72return Te6edf3pathTb4b4b4.Te6edf3joinTb4b4b4(Te6edf3portableExecutableDirTb4b4b4, Ta5d6ff".reticulum-meshchat"Tb4b4b4)Tb4b4b4;
Tb4b4b4}

T8b949e// otherwise, we will fall back to putting the storage dir in the users home directory
T8b949e// e.g: ~/.reticulum-meshchat
Tff7b72return Te6edf3pathTb4b4b4.Te6edf3joinTb4b4b4(Te6edf3appTb4b4b4.Te6edf3getPathTb4b4b4(Ta5d6ff"home"Tb4b4b4)Tb4b4b4, Ta5d6ff".reticulum-meshchat"Tb4b4b4)Tb4b4b4;
Tb4b4b4}

Tff7b72function Te6edf3getDefaultReticulumConfigDirTb4b4b4(Tb4b4b4) Tb4b4b4{
T8b949e// if we are running a windows portable exe, we want to use .reticulum in the portable exe dir
T8b949e// e.g if we launch "E:\Some\Path\MeshChat.exe" we want to use "E:\Some\Path\.reticulum"
Tff7b72const Te6edf3portableExecutableDir Tff7b72= Te6edf3processTb4b4b4.Te6edf3envTb4b4b4.Te6edf3PORTABLE_EXECUTABLE_DIRTb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3processTb4b4b4.Te6edf3platform Tff7b72=== Ta5d6ff"win32" Tff7b72&& Te6edf3portableExecutableDir Tff7b72!= Tff7b72nullTb4b4b4) Tb4b4b4{
Tff7b72return Te6edf3pathTb4b4b4.Te6edf3joinTb4b4b4(Te6edf3portableExecutableDirTb4b4b4, Ta5d6ff".reticulum"Tb4b4b4)Tb4b4b4;
Tb4b4b4}

T8b949e// otherwise, we will fall back to using the .reticulum folder in the users home directory
T8b949e// e.g: ~/.reticulum
Tff7b72return Te6edf3pathTb4b4b4.Te6edf3joinTb4b4b4(Te6edf3appTb4b4b4.Te6edf3getPathTb4b4b4(Ta5d6ff"home"Tb4b4b4)Tb4b4b4, Ta5d6ff".reticulum"Tb4b4b4)Tb4b4b4;
Tb4b4b4}

Tff7b72function Te6edf3createTrayTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72const Te6edf3iconPath Tff7b72= Te6edf3pathTb4b4b4.Te6edf3joinTb4b4b4(Te6edf3__dirnameTb4b4b4, Ta5d6ff"build"Tb4b4b4, Ta5d6ff"icon.png"Tb4b4b4)Tb4b4b4;
Tff7b72const Te6edf3fallbackIconPath Tff7b72= Te6edf3pathTb4b4b4.Te6edf3joinTb4b4b4(Te6edf3__dirnameTb4b4b4, Ta5d6ff"assets"Tb4b4b4, Ta5d6ff"images"Tb4b4b4, Ta5d6ff"logo.png"Tb4b4b4)Tb4b4b4;
Tff7b72const Te6edf3trayIcon Tff7b72= Te6edf3fsTb4b4b4.Te6edf3existsSyncTb4b4b4(Te6edf3iconPathTb4b4b4) Tff7b72? Te6edf3iconPath Tff7b72: Te6edf3fallbackIconPathTb4b4b4;

Te6edf3tray Tff7b72= Tff7b72new Te6edf3TrayTb4b4b4(Te6edf3trayIconTb4b4b4)Tb4b4b4;
Tff7b72const Te6edf3contextMenu Tff7b72= Te6edf3MenuTb4b4b4.Te6edf3buildFromTemplateTb4b4b4(Tb4b4b4[
Tb4b4b4{
Te6edf3labelTff7b72: Ta5d6ff"Show App"Tb4b4b4,
Te6edf3clickTff7b72: Tff7b72function Tb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3mainWindowTb4b4b4) Tb4b4b4{
Te6edf3mainWindowTb4b4b4.Te6edf3showTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Tb4b4b4}Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4{
Te6edf3labelTff7b72: Ta5d6ff"Quit"Tb4b4b4,
Te6edf3clickTff7b72: Tff7b72function Tb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3isQuiting Tff7b72= Tff7b72trueTb4b4b4;
Te6edf3quitTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4}Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4]Tb4b4b4)Tb4b4b4;

Te6edf3trayTb4b4b4.Te6edf3setToolTipTb4b4b4(Ta5d6ff"Reticulum MeshChatX"Tb4b4b4)Tb4b4b4;
Te6edf3trayTb4b4b4.Te6edf3setContextMenuTb4b4b4(Te6edf3contextMenuTb4b4b4)Tb4b4b4;

Te6edf3trayTb4b4b4.Te6edf3onTb4b4b4(Ta5d6ff"click"Tb4b4b4, Tb4b4b4(Tb4b4b4) Tb4b4b4=> Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3mainWindowTb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3mainWindowTb4b4b4.Te6edf3isVisibleTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3mainWindowTb4b4b4.Te6edf3hideTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3mainWindowTb4b4b4.Te6edf3showTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}Tb4b4b4)Tb4b4b4;
Tb4b4b4}

Te6edf3appTb4b4b4.Te6edf3whenReadyTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3thenTb4b4b4(Tff7b72async Tb4b4b4(Tb4b4b4) Tb4b4b4=> Tb4b4b4{
T8b949e// Security: Enforce CSP for all requests as a shell-level fallback
Te6edf3sessionTb4b4b4.Te6edf3defaultSessionTb4b4b4.Te6edf3webRequestTb4b4b4.Te6edf3onHeadersReceivedTb4b4b4(Tb4b4b4(Te6edf3detailsTb4b4b4, Te6edf3callbackTb4b4b4) Tb4b4b4=> Tb4b4b4{
Tff7b72const Te6edf3responseHeaders Tff7b72= Tb4b4b4{ Tb4b4b4...Te6edf3detailsTb4b4b4.Te6edf3responseHeaders Tb4b4b4}Tb4b4b4;

T8b949e// Define a robust fallback CSP that matches our backend's policy
Tff7b72const Te6edf3fallbackCsp Tff7b72= Tb4b4b4[
Ta5d6ff"default-src 'self'"Tb4b4b4,
Ta5d6ff"script-src 'self' 'unsafe-inline' 'unsafe-eval'"Tb4b4b4,
Ta5d6ff"style-src 'self' 'unsafe-inline'"Tb4b4b4,
Ta5d6ff"img-src 'self' data: blob: https://*.tile.openstreetmap.org https://tile.openstreetmap.org https://*.cartocdn.com"Tb4b4b4,
Ta5d6ff"font-src 'self' data:"Tb4b4b4,
Ta5d6ff"connect-src 'self' http://localhost:9337 https://localhost:9337 ws://localhost:* wss://localhost:* blob: https://*.tile.openstreetmap.org https://tile.openstreetmap.org https://nominatim.openstreetmap.org https://git.quad4.io https://*.cartocdn.com"Tb4b4b4,
Ta5d6ff"media-src 'self' blob:"Tb4b4b4,
Ta5d6ff"worker-src 'self' blob:"Tb4b4b4,
Ta5d6ff"frame-src 'self'"Tb4b4b4,
Ta5d6ff"object-src 'none'"Tb4b4b4,
Ta5d6ff"base-uri 'self'"Tb4b4b4,
Tb4b4b4]Tb4b4b4.Te6edf3joinTb4b4b4(Ta5d6ff"; "Tb4b4b4)Tb4b4b4;

T8b949e// If the response doesn't already have a CSP, apply our fallback
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3responseHeadersTb4b4b4[Ta5d6ff"Content-Security-Policy"Tb4b4b4] Tff7b72&& Tff7b72!Te6edf3responseHeadersTb4b4b4[Ta5d6ff"content-security-policy"Tb4b4b4]Tb4b4b4) Tb4b4b4{
Te6edf3responseHeadersTb4b4b4[Ta5d6ff"Content-Security-Policy"Tb4b4b4] Tff7b72= Tb4b4b4[Te6edf3fallbackCspTb4b4b4]Tb4b4b4;
Tb4b4b4}

Te6edf3callbackTb4b4b4(Tb4b4b4{ Te6edf3responseHeaders Tb4b4b4}Tb4b4b4)Tb4b4b4;
Tb4b4b4}Tb4b4b4)Tb4b4b4;

T8b949e// Log Hardware Acceleration status (New in Electron 39)
Tff7b72const Te6edf3isHardwareAccelerationEnabled Tff7b72= Te6edf3appTb4b4b4.Te6edf3isHardwareAccelerationEnabledTb4b4b4(Tb4b4b4)Tb4b4b4;
Te6edf3logTb4b4b4(Ta5d6ff`Ta5d6ffHardware Acceleration Enabled: Tffd700${Te6edf3isHardwareAccelerationEnabledTffd700}Ta5d6ff`Tb4b4b4)Tb4b4b4;

T8b949e// Create system tray
Te6edf3createTrayTb4b4b4(Tb4b4b4)Tb4b4b4;

T8b949e// get arguments passed to application, and remove the provided application path
Tff7b72const Te6edf3ignoredArguments Tff7b72= Tb4b4b4[Ta5d6ff"--no-sandbox"Tb4b4b4, Ta5d6ff"--ozone-platform-hint=auto"Tb4b4b4]Tb4b4b4;
Tff7b72const Te6edf3userProvidedArguments Tff7b72= Te6edf3processTb4b4b4.Te6edf3argvTb4b4b4.Te6edf3sliceTb4b4b4(T79c0ff1Tb4b4b4)Tb4b4b4.Te6edf3filterTb4b4b4(Tb4b4b4(Te6edf3argTb4b4b4) Tb4b4b4=> Tff7b72!Te6edf3ignoredArgumentsTb4b4b4.Te6edf3includesTb4b4b4(Te6edf3argTb4b4b4)Tb4b4b4)Tb4b4b4;
Tff7b72const Te6edf3shouldLaunchHeadless Tff7b72= Te6edf3userProvidedArgumentsTb4b4b4.Te6edf3includesTb4b4b4(Ta5d6ff"--headless"Tb4b4b4)Tb4b4b4;

Tff7b72if Tb4b4b4(Tff7b72!Te6edf3shouldLaunchHeadlessTb4b4b4) Tb4b4b4{
T8b949e// create browser window
Te6edf3mainWindow Tff7b72= Tff7b72new Te6edf3BrowserWindowTb4b4b4(Tb4b4b4{
Te6edf3widthTff7b72: T79c0ff1500Tb4b4b4,
Te6edf3heightTff7b72: T79c0ff800Tb4b4b4,
Te6edf3webPreferencesTff7b72: Tb4b4b4{
T8b949e// used to inject logging over ipc
Te6edf3preloadTff7b72: Te6edf3pathTb4b4b4.Te6edf3joinTb4b4b4(Te6edf3__dirnameTb4b4b4, Ta5d6ff"preload.js"Tb4b4b4)Tb4b4b4,
T8b949e// Security: disable node integration in renderer
Te6edf3nodeIntegrationTff7b72: Tff7b72falseTb4b4b4,
T8b949e// Security: enable context isolation (default in Electron 12+)
Te6edf3contextIsolationTff7b72: Tff7b72trueTb4b4b4,
T8b949e// Security: enable sandbox for additional protection
Te6edf3sandboxTff7b72: Tff7b72trueTb4b4b4,
T8b949e// Security: disable remote module (deprecated but explicit)
Te6edf3enableRemoteModuleTff7b72: Tff7b72falseTb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4}Tb4b4b4)Tb4b4b4;

T8b949e// minimize to tray behavior
Te6edf3mainWindowTb4b4b4.Te6edf3onTb4b4b4(Ta5d6ff"close"Tb4b4b4, Tb4b4b4(Te6edf3eventTb4b4b4) Tb4b4b4=> Tb4b4b4{
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3isQuitingTb4b4b4) Tb4b4b4{
Te6edf3eventTb4b4b4.Te6edf3preventDefaultTb4b4b4(Tb4b4b4)Tb4b4b4;
Te6edf3mainWindowTb4b4b4.Te6edf3hideTb4b4b4(Tb4b4b4)Tb4b4b4;
Tff7b72return Tff7b72falseTb4b4b4;
Tb4b4b4}
Tb4b4b4}Tb4b4b4)Tb4b4b4;

T8b949e// open external links in default web browser instead of electron
Te6edf3mainWindowTb4b4b4.Te6edf3webContentsTb4b4b4.Te6edf3setWindowOpenHandlerTb4b4b4(Tb4b4b4(Tb4b4b4{ Te6edf3url Tb4b4b4}Tb4b4b4) Tb4b4b4=> Tb4b4b4{
Tff7b72var Te6edf3shouldShowInNewElectronWindow Tff7b72= Tff7b72falseTb4b4b4;

T8b949e// we want to open call.html in a new electron window
T8b949e// but all other target="_blank" links should open in the system web browser
T8b949e// we don't want /rnode-flasher/index.html to open in electron, otherwise user can't select usb devices...
Tff7b72if Tb4b4b4(
Tb4b4b4(Te6edf3urlTb4b4b4.Te6edf3startsWithTb4b4b4(Ta5d6ff"http://localhost"Tb4b4b4) Tff7b72|| Te6edf3urlTb4b4b4.Te6edf3startsWithTb4b4b4(Ta5d6ff"https://localhost"Tb4b4b4)Tb4b4b4) Tff7b72&&
Te6edf3urlTb4b4b4.Te6edf3includesTb4b4b4(Ta5d6ff"/call.html"Tb4b4b4)
Tb4b4b4) Tb4b4b4{
Te6edf3shouldShowInNewElectronWindow Tff7b72= Tff7b72trueTb4b4b4;
Tb4b4b4}

T8b949e// we want to open blob urls in a new electron window
Tff7b72else Tff7b72if Tb4b4b4(Te6edf3urlTb4b4b4.Te6edf3startsWithTb4b4b4(Ta5d6ff"blob:"Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3shouldShowInNewElectronWindow Tff7b72= Tff7b72trueTb4b4b4;
Tb4b4b4}

T8b949e// open in new electron window
Tff7b72if Tb4b4b4(Te6edf3shouldShowInNewElectronWindowTb4b4b4) Tb4b4b4{
Tff7b72return Tb4b4b4{
Te6edf3actionTff7b72: Ta5d6ff"allow"Tb4b4b4,
Te6edf3overrideBrowserWindowOptionsTff7b72: Tb4b4b4{
Te6edf3autoHideMenuBarTff7b72: Tff7b72trueTb4b4b4,
Te6edf3webPreferencesTff7b72: Tb4b4b4{
Te6edf3preloadTff7b72: Te6edf3pathTb4b4b4.Te6edf3joinTb4b4b4(Te6edf3__dirnameTb4b4b4, Ta5d6ff"preload.js"Tb4b4b4)Tb4b4b4,
Te6edf3nodeIntegrationTff7b72: Tff7b72falseTb4b4b4,
Te6edf3contextIsolationTff7b72: Tff7b72trueTb4b4b4,
Te6edf3sandboxTff7b72: Tff7b72trueTb4b4b4,
Te6edf3enableRemoteModuleTff7b72: Tff7b72falseTb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4}Tb4b4b4;
Tb4b4b4}

T8b949e// fallback to opening any other url in external browser
Te6edf3shellTb4b4b4.Te6edf3openExternalTb4b4b4(Te6edf3urlTb4b4b4)Tb4b4b4;
Tff7b72return Tb4b4b4{
Te6edf3actionTff7b72: Ta5d6ff"deny"Tb4b4b4,
Tb4b4b4}Tb4b4b4;
Tb4b4b4}Tb4b4b4)Tb4b4b4;

T8b949e// navigate to loading page
Tff7b72await Te6edf3mainWindowTb4b4b4.Te6edf3loadFileTb4b4b4(Te6edf3pathTb4b4b4.Te6edf3joinTb4b4b4(Te6edf3__dirnameTb4b4b4, Ta5d6ff"loading.html"Tb4b4b4)Tb4b4b4)Tb4b4b4;

T8b949e// ask mac users for microphone access for audio calls to work
Tff7b72if Tb4b4b4(Te6edf3processTb4b4b4.Te6edf3platform Tff7b72=== Ta5d6ff"darwin"Tb4b4b4) Tb4b4b4{
Tff7b72await Te6edf3systemPreferencesTb4b4b4.Te6edf3askForMediaAccessTb4b4b4(Ta5d6ff"microphone"Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Tb4b4b4}

T8b949e// find path to python/cxfreeze executable (setup.py builds ReticulumMeshChatX)
Tff7b72const Te6edf3exeName Tff7b72= Te6edf3processTb4b4b4.Te6edf3platform Tff7b72=== Ta5d6ff"win32" Tff7b72? Ta5d6ff"ReticulumMeshChatX.exe" Tff7b72: Ta5d6ff"ReticulumMeshChatX"Tb4b4b4;

T8b949e// get app path (handles both development and packaged app)
Tff7b72const Te6edf3appPath Tff7b72= Te6edf3appTb4b4b4.Te6edf3getAppPathTb4b4b4(Tb4b4b4)Tb4b4b4;
T8b949e// get resources path (where extraFiles are placed)
Tff7b72const Te6edf3resourcesPath Tff7b72= Te6edf3processTb4b4b4.Te6edf3resourcesPath Tff7b72|| Te6edf3pathTb4b4b4.Te6edf3joinTb4b4b4(Te6edf3appPathTb4b4b4, Ta5d6ff".."Tb4b4b4, Ta5d6ff".."Tb4b4b4)Tb4b4b4;
Tff7b72var Te6edf3exe Tff7b72= Tff7b72nullTb4b4b4;

T8b949e// when packaged, extraResources are placed at resources/backend
T8b949e// when packaged with extraFiles, they were at resources/app/electron/build/exe
T8b949e// when packaged with asar, unpacked files are in app.asar.unpacked/ directory
T8b949e// app.getAppPath() returns the path to app.asar, so unpacked is at the same level
Tff7b72const Te6edf3possiblePaths Tff7b72= Tb4b4b4[
T8b949e// packaged app - extraResources location (resources/backend)
Te6edf3pathTb4b4b4.Te6edf3joinTb4b4b4(Te6edf3resourcesPathTb4b4b4, Ta5d6ff"backend"Tb4b4b4, Te6edf3exeNameTb4b4b4)Tb4b4b4,
T8b949e// electron-forge extraResource location (resources/exe)
Te6edf3pathTb4b4b4.Te6edf3joinTb4b4b4(Te6edf3resourcesPathTb4b4b4, Ta5d6ff"exe"Tb4b4b4, Te6edf3exeNameTb4b4b4)Tb4b4b4,
T8b949e// legacy packaged app - extraFiles location (resources/app/electron/build/exe)
Te6edf3pathTb4b4b4.Te6edf3joinTb4b4b4(Te6edf3resourcesPathTb4b4b4, Ta5d6ff"app"Tb4b4b4, Ta5d6ff"electron"Tb4b4b4, Ta5d6ff"build"Tb4b4b4, Ta5d6ff"exe"Tb4b4b4, Te6edf3exeNameTb4b4b4)Tb4b4b4,
T8b949e// packaged app with asar (unpacked files from asarUnpack)
Te6edf3pathTb4b4b4.Te6edf3joinTb4b4b4(Te6edf3appPathTb4b4b4, Ta5d6ff".."Tb4b4b4, Ta5d6ff"app.asar.unpacked"Tb4b4b4, Ta5d6ff"build"Tb4b4b4, Ta5d6ff"exe"Tb4b4b4, Te6edf3exeNameTb4b4b4)Tb4b4b4,
T8b949e// packaged app without asar (relative to app path)
Te6edf3pathTb4b4b4.Te6edf3joinTb4b4b4(Te6edf3appPathTb4b4b4, Ta5d6ff"build"Tb4b4b4, Ta5d6ff"exe"Tb4b4b4, Te6edf3exeNameTb4b4b4)Tb4b4b4,
T8b949e// development mode (relative to electron directory)
Te6edf3pathTb4b4b4.Te6edf3joinTb4b4b4(Te6edf3__dirnameTb4b4b4, Ta5d6ff"build"Tb4b4b4, Ta5d6ff"exe"Tb4b4b4, Te6edf3exeNameTb4b4b4)Tb4b4b4,
T8b949e// development mode (relative to project root)
Te6edf3pathTb4b4b4.Te6edf3joinTb4b4b4(Te6edf3__dirnameTb4b4b4, Ta5d6ff".."Tb4b4b4, Ta5d6ff"build"Tb4b4b4, Ta5d6ff"exe"Tb4b4b4, Te6edf3exeNameTb4b4b4)Tb4b4b4,
Tb4b4b4]Tb4b4b4;

T8b949e// find the first path that exists
Tff7b72for Tb4b4b4(Tff7b72const Te6edf3possibleExe Tff7b72of Te6edf3possiblePathsTb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3fsTb4b4b4.Te6edf3existsSyncTb4b4b4(Te6edf3possibleExeTb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3exe Tff7b72= Te6edf3possibleExeTb4b4b4;
Tff7b72breakTb4b4b4;
Tb4b4b4}
Tb4b4b4}

T8b949e// verify executable exists
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3exe Tff7b72|| Tff7b72!Te6edf3fsTb4b4b4.Te6edf3existsSyncTb4b4b4(Te6edf3exeTb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72const Te6edf3errorMsg Tff7b72= Ta5d6ff`Ta5d6ffCould not find executable: Tffd700${Te6edf3exeNameTffd700}Ta5d6ff\nTa5d6ffChecked paths:Ta5d6ff\nTffd700${Te6edf3possiblePathsTb4b4b4.Te6edf3joinTb4b4b4(Ta5d6ff"\n"Tb4b4b4)Tffd700}Ta5d6ff\nTa5d6ff\nTa5d6ffApp path: Tffd700${Te6edf3appPathTffd700}Ta5d6ff\nTa5d6ffResources path: Tffd700${Te6edf3resourcesPathTffd700}Ta5d6ff`Tb4b4b4;
Te6edf3logTb4b4b4(Te6edf3errorMsgTb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3mainWindowTb4b4b4) Tb4b4b4{
Tff7b72await Te6edf3dialogTb4b4b4.Te6edf3showMessageBoxTb4b4b4(Te6edf3mainWindowTb4b4b4, Tb4b4b4{
Te6edf3messageTff7b72: Te6edf3errorMsgTb4b4b4,
Tb4b4b4}Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Te6edf3appTb4b4b4.Te6edf3quitTb4b4b4(Tb4b4b4)Tb4b4b4;
Tff7b72returnTb4b4b4;
Tb4b4b4}

Te6edf3logTb4b4b4(Ta5d6ff`Ta5d6ffFound executable at: Tffd700${Te6edf3exeTffd700}Ta5d6ff`Tb4b4b4)Tb4b4b4;

T8b949e// Verify backend integrity before spawning
Tff7b72const Te6edf3exeDir Tff7b72= Te6edf3pathTb4b4b4.Te6edf3dirnameTb4b4b4(Te6edf3exeTb4b4b4)Tb4b4b4;
Te6edf3integrityStatusTb4b4b4.Te6edf3backend Tff7b72= Te6edf3verifyBackendIntegrityTb4b4b4(Te6edf3exeDirTb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3integrityStatusTb4b4b4.Te6edf3backendTb4b4b4.Te6edf3okTb4b4b4) Tb4b4b4{
Te6edf3logTb4b4b4(Ta5d6ff`Ta5d6ffINTEGRITY WARNING: Backend tampering detected! Issues: Tffd700${Te6edf3integrityStatusTb4b4b4.Te6edf3backendTb4b4b4.Te6edf3issuesTb4b4b4.Te6edf3joinTb4b4b4(Ta5d6ff", "Tb4b4b4)Tffd700}Ta5d6ff`Tb4b4b4)Tb4b4b4;
Tb4b4b4}

Tff7b72try Tb4b4b4{
T8b949e// arguments we always want to pass in
Tff7b72const Te6edf3requiredArguments Tff7b72= Tb4b4b4[
Ta5d6ff"--headless"Tb4b4b4, T8b949e// reticulum meshchatx usually launches default web browser, we don't want this when using electron
Ta5d6ff"--port"Tb4b4b4,
Ta5d6ff"9337"Tb4b4b4,
T8b949e// '--test-exception-message', 'Test Exception Message', // uncomment to test the crash dialog
Tb4b4b4]Tb4b4b4;

T8b949e// if user didn't provide reticulum config dir, we should provide it
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3userProvidedArgumentsTb4b4b4.Te6edf3includesTb4b4b4(Ta5d6ff"--reticulum-config-dir"Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3requiredArgumentsTb4b4b4.Te6edf3pushTb4b4b4(Ta5d6ff"--reticulum-config-dir"Tb4b4b4, Te6edf3getDefaultReticulumConfigDirTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4;
Tb4b4b4}

T8b949e// if user didn't provide storage dir, we should provide it
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3userProvidedArgumentsTb4b4b4.Te6edf3includesTb4b4b4(Ta5d6ff"--storage-dir"Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3requiredArgumentsTb4b4b4.Te6edf3pushTb4b4b4(Ta5d6ff"--storage-dir"Tb4b4b4, Te6edf3getDefaultStorageDirTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4;
Tb4b4b4}

T8b949e// spawn executable
Te6edf3exeChildProcess Tff7b72= Te6edf3spawnTb4b4b4(Te6edf3exeTb4b4b4, Tb4b4b4[
Tb4b4b4...Te6edf3requiredArgumentsTb4b4b4, T8b949e// always provide required arguments
Tb4b4b4...Te6edf3userProvidedArgumentsTb4b4b4, T8b949e// also include any user provided arguments
Tb4b4b4]Tb4b4b4)Tb4b4b4;

Tff7b72if Tb4b4b4(Tff7b72!Te6edf3exeChildProcess Tff7b72|| Tff7b72!Te6edf3exeChildProcessTb4b4b4.Te6edf3pidTb4b4b4) Tb4b4b4{
Tff7b72throw Tff7b72new Tf85149ErrorTb4b4b4(Ta5d6ff"Failed to start backend process (no PID)."Tb4b4b4)Tb4b4b4;
Tb4b4b4}

T8b949e// log stdout
Tff7b72var Te6edf3stdoutLines Tff7b72= Tb4b4b4[Tb4b4b4]Tb4b4b4;
Te6edf3exeChildProcessTb4b4b4.Te6edf3stdoutTb4b4b4.Te6edf3setEncodingTb4b4b4(Ta5d6ff"utf8"Tb4b4b4)Tb4b4b4;
Te6edf3exeChildProcessTb4b4b4.Te6edf3stdoutTb4b4b4.Te6edf3onTb4b4b4(Ta5d6ff"data"Tb4b4b4, Tff7b72function Tb4b4b4(Te6edf3dataTb4b4b4) Tb4b4b4{
T8b949e// log
Te6edf3logTb4b4b4(Te6edf3dataTb4b4b4.Te6edf3toStringTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4;

T8b949e// keep track of last 100 stdout lines
Te6edf3stdoutLinesTb4b4b4.Te6edf3pushTb4b4b4(Te6edf3dataTb4b4b4.Te6edf3toStringTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3stdoutLinesTb4b4b4.Te6edf3length Tff7b72> T79c0ff100Tb4b4b4) Tb4b4b4{
Te6edf3stdoutLinesTb4b4b4.Te6edf3shiftTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Tb4b4b4}Tb4b4b4)Tb4b4b4;

T8b949e// log stderr
Tff7b72var Te6edf3stderrLines Tff7b72= Tb4b4b4[Tb4b4b4]Tb4b4b4;
Te6edf3exeChildProcessTb4b4b4.Te6edf3stderrTb4b4b4.Te6edf3setEncodingTb4b4b4(Ta5d6ff"utf8"Tb4b4b4)Tb4b4b4;
Te6edf3exeChildProcessTb4b4b4.Te6edf3stderrTb4b4b4.Te6edf3onTb4b4b4(Ta5d6ff"data"Tb4b4b4, Tff7b72function Tb4b4b4(Te6edf3dataTb4b4b4) Tb4b4b4{
T8b949e// log
Te6edf3logTb4b4b4(Te6edf3dataTb4b4b4.Te6edf3toStringTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4;

T8b949e// keep track of last 100 stderr lines
Te6edf3stderrLinesTb4b4b4.Te6edf3pushTb4b4b4(Te6edf3dataTb4b4b4.Te6edf3toStringTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3stderrLinesTb4b4b4.Te6edf3length Tff7b72> T79c0ff100Tb4b4b4) Tb4b4b4{
Te6edf3stderrLinesTb4b4b4.Te6edf3shiftTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Tb4b4b4}Tb4b4b4)Tb4b4b4;

T8b949e// log errors
Te6edf3exeChildProcessTb4b4b4.Te6edf3onTb4b4b4(Ta5d6ff"error"Tb4b4b4, Tff7b72function Tb4b4b4(Te6edf3errorTb4b4b4) Tb4b4b4{
Te6edf3logTb4b4b4(Te6edf3errorTb4b4b4)Tb4b4b4;
Tb4b4b4}Tb4b4b4)Tb4b4b4;

T8b949e// quit electron app if exe dies
Te6edf3exeChildProcessTb4b4b4.Te6edf3onTb4b4b4(Ta5d6ff"exit"Tb4b4b4, Tff7b72async Tff7b72function Tb4b4b4(Te6edf3codeTb4b4b4) Tb4b4b4{
T8b949e// if no exit code provided, we wanted exit to happen, so do nothing
Tff7b72if Tb4b4b4(Te6edf3code Tff7b72== Tff7b72nullTb4b4b4) Tb4b4b4{
Tff7b72returnTb4b4b4;
Tb4b4b4}

T8b949e// show crash log
Tff7b72const Te6edf3stdout Tff7b72= Te6edf3stdoutLinesTb4b4b4.Te6edf3joinTb4b4b4(Ta5d6ff""Tb4b4b4)Tb4b4b4;
Tff7b72const Te6edf3stderr Tff7b72= Te6edf3stderrLinesTb4b4b4.Te6edf3joinTb4b4b4(Ta5d6ff""Tb4b4b4)Tb4b4b4;

T8b949e// Base64 encode for safe URL passing
Tff7b72const Te6edf3stdoutBase64 Tff7b72= Te6edf3BufferTb4b4b4.Tff7b72fromTb4b4b4(Te6edf3stdoutTb4b4b4)Tb4b4b4.Te6edf3toStringTb4b4b4(Ta5d6ff"base64"Tb4b4b4)Tb4b4b4;
Tff7b72const Te6edf3stderrBase64 Tff7b72= Te6edf3BufferTb4b4b4.Tff7b72fromTb4b4b4(Te6edf3stderrTb4b4b4)Tb4b4b4.Te6edf3toStringTb4b4b4(Ta5d6ff"base64"Tb4b4b4)Tb4b4b4;

T8b949e// Load crash page if main window exists
Tff7b72if Tb4b4b4(Te6edf3mainWindow Tff7b72&& Tff7b72!Te6edf3mainWindowTb4b4b4.Te6edf3isDestroyedTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3mainWindowTb4b4b4.Te6edf3showTb4b4b4(Tb4b4b4)Tb4b4b4; T8b949e// Ensure visible
Te6edf3mainWindowTb4b4b4.Te6edf3focusTb4b4b4(Tb4b4b4)Tb4b4b4;
Tff7b72await Te6edf3mainWindowTb4b4b4.Te6edf3loadFileTb4b4b4(Te6edf3pathTb4b4b4.Te6edf3joinTb4b4b4(Te6edf3__dirnameTb4b4b4, Ta5d6ff"crash.html"Tb4b4b4)Tb4b4b4, Tb4b4b4{
Te6edf3queryTff7b72: Tb4b4b4{
Te6edf3codeTff7b72: Te6edf3codeTb4b4b4.Te6edf3toStringTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3stdoutTff7b72: Te6edf3stdoutBase64Tb4b4b4,
Te6edf3stderrTff7b72: Te6edf3stderrBase64Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4}Tb4b4b4)Tb4b4b4;
Tb4b4b4} Tff7b72else Tb4b4b4{
T8b949e// Fallback for cases where window is gone
Tff7b72await Te6edf3dialogTb4b4b4.Te6edf3showMessageBoxTb4b4b4(Tb4b4b4{
Te6edf3typeTff7b72: Ta5d6ff"error"Tb4b4b4,
Te6edf3titleTff7b72: Ta5d6ff"MeshChatX Crashed"Tb4b4b4,
Te6edf3messageTff7b72: Ta5d6ff`Ta5d6ffBackend exited with code: Tffd700${Te6edf3codeTffd700}Ta5d6ff\nTa5d6ff\nTa5d6ffSTDOUT: Tffd700${Te6edf3stdoutTb4b4b4.Te6edf3sliceTb4b4b4(Tff7b72-T79c0ff500Tb4b4b4)Tffd700}Ta5d6ff\nTa5d6ff\nTa5d6ffSTDERR: Tffd700${Te6edf3stderrTb4b4b4.Te6edf3sliceTb4b4b4(Tff7b72-T79c0ff500Tb4b4b4)Tffd700}Ta5d6ff`Tb4b4b4,
Tb4b4b4}Tb4b4b4)Tb4b4b4;
Te6edf3appTb4b4b4.Te6edf3quitTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Tb4b4b4}Tb4b4b4)Tb4b4b4;
Tb4b4b4} Tff7b72catch Tb4b4b4(Te6edf3eTb4b4b4) Tb4b4b4{
Te6edf3logTb4b4b4(Te6edf3eTb4b4b4)Tb4b4b4;
Tb4b4b4}
Tb4b4b4}Tb4b4b4)Tb4b4b4;

Tff7b72function Te6edf3quitTb4b4b4(Tb4b4b4) Tb4b4b4{
T8b949e// kill python process
Tff7b72if Tb4b4b4(Te6edf3exeChildProcessTb4b4b4) Tb4b4b4{
Te6edf3exeChildProcessTb4b4b4.Te6edf3killTb4b4b4(Ta5d6ff"SIGKILL"Tb4b4b4)Tb4b4b4;
Tb4b4b4}

T8b949e// quit electron app
Te6edf3appTb4b4b4.Te6edf3quitTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4}

T8b949e// quit electron if all windows are closed
Te6edf3appTb4b4b4.Te6edf3onTb4b4b4(Ta5d6ff"window-all-closed"Tb4b4b4, Tb4b4b4(Tb4b4b4) Tb4b4b4=> Tb4b4b4{
Te6edf3quitTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4}Tb4b4b4)Tb4b4b4;

T8b949e// make sure child process is killed if app is quiting
Te6edf3appTb4b4b4.Te6edf3onTb4b4b4(Ta5d6ff"quit"Tb4b4b4, Tb4b4b4(Tb4b4b4) Tb4b4b4=> Tb4b4b4{
Te6edf3quitTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4}Tb4b4b4)Tb4b4b4;


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────